#include "drv_spinor.h"

#include "bsp_gpio.h"
#include "string.h"

/* Winbond SPIFalsh ID */
#define W25Q80  0XEF13
#define W25Q16  0XEF14
#define W25Q32  0XEF15
#define W25Q64  0XEF16
#define W25Q128 0XEF17

/* Winbond SPIFalsh Instruction List */
#define W25X_WriteEnable      0x06
#define W25X_WriteDisable     0x04
#define W25X_ReadStatusReg    0x05
#define W25X_WriteStatusReg   0x01
#define W25X_ReadData         0x03
#define W25X_FastReadData     0x0B
#define W25X_FastReadDual     0x3B
#define W25X_PageProgram      0x02
#define W25X_BlockErase       0xD8
#define W25X_SectorErase      0x20
#define W25X_ChipErase        0xC7
#define W25X_PowerDown        0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID         0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID    0x9F

static uint8_t SPINor_DMATxBuf[16];
static uint8_t SPINor_DMARxBuf[16];

/**
 * @brief 
 * 
 */
void SPINor_Init(void)
{
    SPI_InitTypeDef SPI_InitStructure = { 0 };

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
    /*  */
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPINOR_SPI_NUM, &SPI_InitStructure);
    SPINOR_SPI_NUM->HSCR = 0x01;

    SPI_Cmd(SPINOR_SPI_NUM, ENABLE);

    SPI_I2S_DMACmd(SPINOR_SPI_NUM, SPI_I2S_DMAReq_Tx, ENABLE);
    SPI_I2S_DMACmd(SPINOR_SPI_NUM, SPI_I2S_DMAReq_Rx, ENABLE);
}

/*********************************************************************
 * @fn      DMA_Tx_Init
 *
 * @brief   Initializes the DMAy Channelx configuration.
 *
 * @param   DMA_CHx - x can be 1 to 7.
 *          ppadr - Peripheral base address.
 *          memadr - Memory base address.
 *          bufsize - DMA channel buffer size.
 *
 * @return  none
 */
void SPINor_DMA_Tx_Init(DMA_Channel_TypeDef *DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize, u32 memoryinc)
{
    DMA_InitTypeDef DMA_InitStructure = { 0 };

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);

    DMA_DeInit(DMA_CHx);

    DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr;
    DMA_InitStructure.DMA_MemoryBaseAddr = memadr;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = bufsize;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = memoryinc;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA_CHx, &DMA_InitStructure);
}

/*********************************************************************
 * @fn      DMA_Rx_Init
 *
 * @brief   Initializes the SPI1 DMA Channelx configuration.
 *
 * @param   DMA_CHx - x can be 1 to 7.
 *          ppadr - Peripheral base address.
 *          memadr - Memory base address.
 *          bufsize - DMA channel buffer size.
 *
 * @return  none
 */
void SPINor_DMA_Rx_Init(DMA_Channel_TypeDef *DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize, u32 memoryinc)
{
    DMA_InitTypeDef DMA_InitStructure = { 0 };

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);

    DMA_DeInit(DMA_CHx);

    DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr;
    DMA_InitStructure.DMA_MemoryBaseAddr = memadr;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = bufsize;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = memoryinc;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA_CHx, &DMA_InitStructure);
}

void SPINor_Wait_DMA(void)
{
    while (DMA_GetFlagStatus(DMA2_FLAG_TC1) == RESET)
        ;
    while (SPI_I2S_GetFlagStatus(SPINOR_SPI_NUM, SPI_I2S_FLAG_BSY) == SET)
        ;
}

void SPINor_Init_DMA(uint16_t len)
{
    // memset(SPINor_DMATxBuf, 0, len);
    // memset(SPINor_DMARxBuf, 0, len);

    SPINor_DMA_Tx_Init(DMA2_Channel2, (u32)&SPINOR_SPI_NUM->DATAR, (u32)SPINor_DMATxBuf, len, DMA_MemoryInc_Enable);
    SPINor_DMA_Rx_Init(DMA2_Channel1, (u32)&SPINOR_SPI_NUM->DATAR, (u32)SPINor_DMARxBuf, len, DMA_MemoryInc_Enable);
    DMA_Cmd(DMA2_Channel2, ENABLE);
    DMA_Cmd(DMA2_Channel1, ENABLE);
}

void SPINor_Init_DMA_Read(u32 rxBuf, uint16_t len)
{
    SPINor_DMATxBuf[0] = 0xff;

    SPINor_DMA_Tx_Init(DMA2_Channel2, (u32)&SPINOR_SPI_NUM->DATAR, (u32)SPINor_DMATxBuf, len, DMA_MemoryInc_Disable);
    SPINor_DMA_Rx_Init(DMA2_Channel1, (u32)&SPINOR_SPI_NUM->DATAR, rxBuf, len, DMA_MemoryInc_Enable);
    DMA_Cmd(DMA2_Channel2, ENABLE);
    DMA_Cmd(DMA2_Channel1, ENABLE);
}

void SPINor_Init_DMA_Write(u32 txBuf, uint16_t len)
{
    SPINor_DMA_Tx_Init(DMA2_Channel2, (u32)&SPINOR_SPI_NUM->DATAR, txBuf, len, DMA_MemoryInc_Enable);
    SPINor_DMA_Rx_Init(DMA2_Channel1, (u32)&SPINOR_SPI_NUM->DATAR, (u32)SPINor_DMARxBuf, len, DMA_MemoryInc_Disable);
    DMA_Cmd(DMA2_Channel2, ENABLE);
    DMA_Cmd(DMA2_Channel1, ENABLE);
}

/**
 * @brief 
 * 
 * @param TxData 
 * @return uint8_t 
 */
uint8_t SPINor_ReadWriteByte(uint8_t TxData)
{
    while (SPI_I2S_GetFlagStatus(SPINOR_SPI_NUM, SPI_I2S_FLAG_TXE) == RESET)
        ;

    SPI_I2S_SendData(SPINOR_SPI_NUM, TxData);

    while (SPI_I2S_GetFlagStatus(SPINOR_SPI_NUM, SPI_I2S_FLAG_RXNE) == RESET)
        ;

    return SPI_I2S_ReceiveData(SPINOR_SPI_NUM);
}

/**
 * @brief 
 * 
 * @return uint8_t 
 */
uint8_t SPINor_ReadSR(void)
{
    uint16_t cmd_len = 0;

    SPINor_DMATxBuf[cmd_len++] = W25X_ReadStatusReg;
    SPINor_DMATxBuf[cmd_len++] = 0xff;

    BSP_SPINor_CS_LOW();

    SPINor_Init_DMA(cmd_len);
    SPINor_Wait_DMA();

    BSP_SPINor_CS_HIGH();

    return SPINor_DMARxBuf[1];
}

/**
 * @brief 
 * 
 * @return uint16_t 
 */
uint16_t SPINor_ReadID(void)
{
    uint16_t Temp = 0;
    uint16_t cmd_len = 0;

    SPINor_DMATxBuf[cmd_len++] = W25X_ManufactDeviceID;
    SPINor_DMATxBuf[cmd_len++] = 0x00;
    SPINor_DMATxBuf[cmd_len++] = 0x00;
    SPINor_DMATxBuf[cmd_len++] = 0x00;
    SPINor_DMATxBuf[cmd_len++] = 0xff;
    SPINor_DMATxBuf[cmd_len++] = 0xff;

    BSP_SPINor_CS_LOW();

    SPINor_Init_DMA(cmd_len);
    SPINor_Wait_DMA();

    Temp |= SPINor_DMARxBuf[4] << 8;
    Temp |= SPINor_DMARxBuf[5];

    BSP_SPINor_CS_HIGH();

    return Temp;
}

/**
 * @brief 
 * 
 * @param id 
 */
void SPINor_ReadUniqueID(uint8_t *id)
{
    uint16_t cmd_len = 0;

    SPINor_DMATxBuf[cmd_len++] = 0x4b;

    SPINor_DMATxBuf[cmd_len++] = 0x00;
    SPINor_DMATxBuf[cmd_len++] = 0x00;
    SPINor_DMATxBuf[cmd_len++] = 0x00;
    SPINor_DMATxBuf[cmd_len++] = 0x00;

    SPINor_DMATxBuf[cmd_len++] = 0xff;
    SPINor_DMATxBuf[cmd_len++] = 0xff;
    SPINor_DMATxBuf[cmd_len++] = 0xff;
    SPINor_DMATxBuf[cmd_len++] = 0xff;
    SPINor_DMATxBuf[cmd_len++] = 0xff;
    SPINor_DMATxBuf[cmd_len++] = 0xff;
    SPINor_DMATxBuf[cmd_len++] = 0xff;
    SPINor_DMATxBuf[cmd_len++] = 0xff;

    BSP_SPINor_CS_LOW();

    SPINor_Init_DMA(cmd_len);
    SPINor_Wait_DMA();

    id[0] = SPINor_DMARxBuf[5];
    id[1] = SPINor_DMARxBuf[6];
    id[2] = SPINor_DMARxBuf[7];
    id[3] = SPINor_DMARxBuf[8];
    id[4] = SPINor_DMARxBuf[9];
    id[5] = SPINor_DMARxBuf[10];
    id[6] = SPINor_DMARxBuf[11];
    id[7] = SPINor_DMARxBuf[12];
    BSP_SPINor_CS_HIGH();
}

/**
 * @brief 
 * 
 * @param id 
 */
void SPINor_ReadJedecID(uint8_t *id)
{
    uint16_t cmd_len = 0;

    SPINor_DMATxBuf[cmd_len++] = W25X_JedecDeviceID;

    SPINor_DMATxBuf[cmd_len++] = 0xFF;
    SPINor_DMATxBuf[cmd_len++] = 0xFF;
    SPINor_DMATxBuf[cmd_len++] = 0xFF;

    BSP_SPINor_CS_LOW();

    SPINor_Init_DMA(cmd_len);
    SPINor_Wait_DMA();

    id[0] = SPINor_DMARxBuf[1];
    id[1] = SPINor_DMARxBuf[2];
    id[2] = SPINor_DMARxBuf[3];

    BSP_SPINor_CS_HIGH();
}

/*********************************************************************
 * @fn      SPINor_Wait_Busy
 *
 * @brief   Wait flash free.
 *
 * @return  none
 */
void SPINor_Wait_Busy(void)
{
    while ((SPINor_ReadSR() & 0x01) == 0x01)
        ;
}

/*********************************************************************
 * @fn      SPI_FLASH_Write_Enable
 *
 * @brief   Enable flash write.
 *
 * @return  none
 */
void SPINor_Write_Enable(void)
{
    SPINor_DMATxBuf[0] = W25X_WriteEnable;

    BSP_SPINor_CS_LOW();
    SPINor_Init_DMA(1);
    SPINor_Wait_DMA();
    BSP_SPINor_CS_HIGH();
}

/*********************************************************************
 * @fn      SPI_FLASH_Write_Disable
 *
 * @brief   Disable flash write.
 *
 * @return  none
 */
void SPINor_Write_Disable(void)
{
    SPINor_DMATxBuf[0] = W25X_WriteDisable;

    BSP_SPINor_CS_LOW();
    SPINor_Init_DMA(1);
    SPINor_Wait_DMA();
    BSP_SPINor_CS_HIGH();
}

/*********************************************************************
 * @fn      SPINor_Read
 *
 * @brief   Read data from flash.
 *
 * @param   pBuffer -
 *          ReadAddr -Initial address(24bit).
 *          size - Data length.
 *
 * @return  none
 */
void SPINor_Read(u8 *pBuffer, u32 ReadAddr, u16 size)
{
    u16 cmd_len = 0;

    SPINor_DMATxBuf[cmd_len++] = W25X_FastReadData;
    SPINor_DMATxBuf[cmd_len++] = (u8)((ReadAddr) >> 16);
    SPINor_DMATxBuf[cmd_len++] = (u8)((ReadAddr) >> 8);
    SPINor_DMATxBuf[cmd_len++] = (u8)ReadAddr;
    SPINor_DMATxBuf[cmd_len++] = (u8)0x00;

    BSP_SPINor_CS_LOW();
    SPINor_Init_DMA(cmd_len);
    SPINor_Wait_DMA();

    SPINor_Init_DMA_Read((u32)pBuffer, size);
    SPINor_Wait_DMA();

    BSP_SPINor_CS_HIGH();
}

/*********************************************************************
 * @fn      SPI_Flash_Erase_Chip
 *
 * @brief   Erase all FLASH pages.
 *
 * @return  none
 */
void SPINor_Erase_Chip(void)
{
    SPINor_Write_Enable();
    SPINor_Wait_Busy();

    SPINor_DMATxBuf[0] = W25X_ChipErase;

    BSP_SPINor_CS_LOW();
    SPINor_Init_DMA(1);
    SPINor_Wait_DMA();
    BSP_SPINor_CS_HIGH();

    SPINor_Wait_Busy();
}

void SPINor_Erase_Sector(u32 Dst_Addr)
{
    SPINor_Write_Enable();
    SPINor_Wait_Busy();

    uint16_t cmd_len = 0;
    SPINor_DMATxBuf[cmd_len++] = W25X_SectorErase;
    SPINor_DMATxBuf[cmd_len++] = (u8)((Dst_Addr) >> 16);
    SPINor_DMATxBuf[cmd_len++] = (u8)((Dst_Addr) >> 8);
    SPINor_DMATxBuf[cmd_len++] = (u8)Dst_Addr;

    BSP_SPINor_CS_LOW();
    SPINor_Init_DMA(cmd_len);
    SPINor_Wait_DMA();
    BSP_SPINor_CS_HIGH();

    SPINor_Wait_Busy();
}

void SPINor_Write_Page(u8 *pBuffer, u32 WriteAddr, u16 size)
{
    SPINor_Write_Enable();

    uint16_t cmd_len = 0;

    SPINor_DMATxBuf[cmd_len++] = W25X_PageProgram;
    SPINor_DMATxBuf[cmd_len++] = (u8)((WriteAddr) >> 16);
    SPINor_DMATxBuf[cmd_len++] = (u8)((WriteAddr) >> 8);
    SPINor_DMATxBuf[cmd_len++] = (u8)WriteAddr;

    BSP_SPINor_CS_LOW();

    SPINor_Init_DMA(cmd_len);
    SPINor_Wait_DMA();

    SPINor_Init_DMA_Write((u32)pBuffer, size);
    SPINor_Wait_DMA();

    BSP_SPINor_CS_HIGH();

    SPINor_Wait_Busy();
}
